package xyz.jcat.redis.config;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import xyz.jcat.common.util.ArrayUtils;

@Configuration
public class RedisConfig {

    private StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    private GenericJackson2JsonRedisSerializer jacksonRedisSerializer = new GenericJackson2JsonRedisSerializer();

//    @Bean
//    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//        redisTemplate.setConnectionFactory(redisConnectionFactory);
//        // key的序列化类型
//        redisTemplate.setKeySerializer(new StringRedisSerializer());
//        // value序列化
//        ProtoStuffRedisSerializer protoStuffRedisSerializer = new ProtoStuffRedisSerializer();
//        redisTemplate.setValueSerializer(protoStuffRedisSerializer);
//        return redisTemplate;
//    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // key的序列化类型
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // value序列化
        redisTemplate.setValueSerializer(jacksonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jacksonRedisSerializer);
        return redisTemplate;
    }

    /**
     * redis ProtoStuff序列化类
     */
    public static class ProtoStuffRedisSerializer implements RedisSerializer<Object> {

        private static final Schema<ObjectWrapper> SCHEMA = RuntimeSchema.getSchema(ObjectWrapper.class);
        private static LinkedBuffer LINKED_BUFFER = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);

        @Override
        public byte[] serialize(Object obj) throws SerializationException {
            if(obj == null) {
                return null;
            }
            byte[] bytes;
            try {
                bytes = ProtostuffIOUtil.toByteArray(new ObjectWrapper(obj), SCHEMA, LINKED_BUFFER);
            } finally {
                LINKED_BUFFER.clear();
            }
            return bytes;
        }

        @Override
        public Object deserialize(byte[] bytes) throws SerializationException {
            if (ArrayUtils.isEmpty(bytes)) {
                return null;
            }
            try {
                ObjectWrapper objectWrapper = new ObjectWrapper();
                ProtostuffIOUtil.mergeFrom(bytes, objectWrapper, SCHEMA);
                return objectWrapper.get();
            } catch (Exception e) {
                throw new SerializationException("redis protostuff deserialize error", e);
            }
        }

        private class ObjectWrapper<V> {

            private V v;

            ObjectWrapper() {
            }

            ObjectWrapper(V v) {
                this.v = v;
            }

            public V get() {
                return v;
            }

            public void set(V v) {
                this.v = v;
            }
        }
    }

}
